﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using StockTrader.Common;
using StockTrader.Platform.Charting;
using StockTrader.Platform.Logging;
using StockTrader.Stocks.General;
using StockTrader.Utilities;
using StockTrader.Utilities.Broker;


// OnEODData = Runs on EOD data (i.e. on a period havin the closing price of each day in that period)
namespace StockTrader.API.TradingAlgos
{
    public class Algo10_MultipleLotBuySellAnalyzer: ITradingAlgo
    {
        StockChart mChart = new StockChart();

        List<StockOrder> mOrders = new List<StockOrder>();
        
        List<Tick> ticks = new List<Tick>();

        List<Tick> allTicks = new List<Tick>();
        List<StockOrder> allOrders = new List<StockOrder>();
        Dictionary<int, Tick> allPeaks = new Dictionary<int, Tick>();
        List<double> profitableOrders = new List<double>(1000);
        List<double> lossOrders = new List<double>(1000);
        List<double> profitableAfterBrokerageOrders = new List<double>(1000);

        // EOD Trade stats
        EODTradeStats EodTradeStats;
        
        readonly double mPercChangeThresholdForMarketDirection;
        readonly double mPercChangeThresholdForSquareOff;

        readonly bool mIsSquareOffTrigger;
        readonly bool mIsMinProfitMust;
        readonly bool mIsMarketClosingSquareOff;

        //if the price moves in the opposite direction (leading to a loss)
        //then mark losses if this is the percentage change.
        readonly double mStopLossPercentage;

        //if the profit percentage is above this price, 
        //then book profit.
        //else, hold on
        readonly double mMinimumProfitPercentage;

        double mAverageTradePrice = 0;
        public int mTotalTickCount = 0;
       
        int mTickLastSquareOffDone = 0;
        readonly bool mUseProbableTradeValue;

        readonly bool mAllowShort;
        readonly bool mAllowLong;
        readonly double mPositionSpacingPerc;

        readonly List<string> mOrderReferenceNumbers = new List<string>();
        readonly bool mMock;
        readonly bool mIsReplayMode;
        bool bReplayDone = false;
        readonly IBroker mBroker;
        readonly string mStockCode;
        readonly int mQuantity;
        readonly InstrumentType mInstrumentType;
        readonly DateTime mExpiryDate;

        //readonly double mPercentageChangeForFreshBuy;
        //readonly double mPercentageChangeForShortSquareOff;
        //readonly double mPercentageChangeForFreshShort;
        //readonly double mPercentageChangeForLongSquareOff;
        readonly double mLongCeilingPrice;
        readonly double mShortFloorPrice;

        readonly int mMaxTotalPositions, mMaxLongPositions, mMaxShortPositions;
        readonly double mBrokerage; // in percentage

        readonly string mTraceFileName;
        readonly string mTickFileName;
        readonly string mReplayTickFile;

        public int TotalTimesLimitShortage = 0;


        // Trade Summary
        public double ltp = 0;
        double mCummulativeProfitBooked = 0;
        double mNettFruitIfSquaredOffAtLTP = 0;

        string mPositionsFile;

        public double TotalProfit { get; private set; }

        public double ExpectedNettProfit { get; private set; }


        public double TotalBrokerage { get; private set; }

        public double TotalTurnover { get; private set; }

        public int TotalBuyTrades { get; private set; }

        public int TotalSellTrades { get; private set; }

        public int TotalFailedOrders { get; private set; }

        DateTime mTimeOfFirstTick;
        DateTime mTimeOfLastTick;

        //// Thread Add Data delegate
        //public delegate void AddDataDelegate(DateTime x, double y);
        public AddTickChartDelegate addDataDel;
        Action<ChartTickPoint> _chartDelegate;
        //public void AddTickToChart(DateTime x, double y)
        //{
        //    object[] parameters = {x, y};

        //    mChart.Invoke(mChart.addDataDel, parameters);
        //}

        // ------- Core Algo methods start -------- //

        public Algo10_MultipleLotBuySellAnalyzer(
            AddTickChartDelegate dataDelegate,
            object chartDelegate,
            bool mock,
            bool bIsReplayMode,
            string replayTickFile,
            string positionsFile,
            bool useProbableTradeValue,
            bool isMarketClosingSquareOff,
            bool isSquareOffTrigger,
            bool isMinProfitMust,
            double percChangeForMarketDirectionChange,
            double percChangeThresholdForSquareOff,
            double stopLossPercentage,
            double minimumProfitPercentage,
            bool allowShort,
            bool allowLong,
            List<StockOrder> startOrders,
            IBroker broker,
            string stockCode,
            int quantity,
            InstrumentType instrumentType,
            DateTime expiryDate,
            int maxTotalPositions,
            int maxLongPositions,
            int maxShortPositions,
            double longCeilingPrice,
            double shortFloorPrice,
            double positionSpacingPerc,
            double brokerage)
        {
            TotalProfit = 0;
            ExpectedNettProfit = 0;
            TotalBrokerage = 0;
            _chartDelegate = (Action<ChartTickPoint>)chartDelegate;

            addDataDel = dataDelegate;

            //addDataDel += new AddDataDelegate(AddTickToChart);
            //mChart.Show();

            mStockCode = stockCode;
            
            string guidId = Guid.NewGuid().ToString();
            mTickFileName = "C:\\StockTickFiles\\" + mStockCode + "-" + DateTime.Today.ToString("yyyyMMdd") + "-" + guidId + ".txt";
            mTraceFileName = "C:\\StockTraderLogs\\EOD-StockTrader-Runs\\Algo10-" + mStockCode + "-" + DateTime.Today.ToString("yyyyMMdd") + "-" + guidId + ".txt";

            mTickFileName = @"E:\StockData\10-Year-EOD-Data-MyFormat-New\" + mStockCode + "-EOD.txt";

            //FileTracing.InitializeTraceSources(mTraceFileName);
            mIsReplayMode = bIsReplayMode;
            mReplayTickFile = replayTickFile;
            mMock = mock;

            mUseProbableTradeValue = useProbableTradeValue;
            mIsMarketClosingSquareOff = false;// isMarketClosingSquareOff; // false for EOD algo
            mIsSquareOffTrigger = isSquareOffTrigger;
            mIsMinProfitMust = isMinProfitMust;
            mPercChangeThresholdForMarketDirection = percChangeForMarketDirectionChange;
            mPercChangeThresholdForSquareOff = percChangeThresholdForSquareOff;
            mStopLossPercentage = stopLossPercentage;
            mMinimumProfitPercentage = minimumProfitPercentage;
            mPositionsFile = positionsFile;
            mAllowShort = allowShort;
            mAllowLong = allowLong;

            if (startOrders != null)
            {
                foreach (StockOrder order in startOrders)
                {
                    if (order.OrderPosition != Position.None)
                    {
                        mOrders.Add(new StockOrder(order));
                    }
                }
            }
            mBroker = broker;

            mQuantity = quantity;
            mInstrumentType = instrumentType;
            mExpiryDate = expiryDate;
            mMaxTotalPositions = maxTotalPositions;
            mMaxLongPositions = maxLongPositions;
            mMaxShortPositions = maxShortPositions;
            mBrokerage = brokerage;
            mLongCeilingPrice = longCeilingPrice;
            mShortFloorPrice = shortFloorPrice;
            mPositionSpacingPerc = positionSpacingPerc;

            FileTracing.TraceInformation("START : " + DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString());
        }

        public class Tick
        {
            public DerivativeQuoteInformation Di;
            public MarketDirection MarketDirection;
            public double PercChangeFromLastTrendingTick;
            public int TickNumber;
            public StockOrder Order;
        }

        double GetTradeValueForAnalysis(DerivativeQuoteInformation di)
        {
            if (mUseProbableTradeValue)
            {
                if (di.ProbableNextTradeValue != -1)
                    return di.ProbableNextTradeValue;
            }
            if (di.LastTradedPriceDouble > 0)
            {
                return di.LastTradedPriceDouble;
            }
            return di.LastTradedPriceDouble;
        }

        public Tick MinTick;
        public Tick MaxTick;
        public MarketDirection lastPeakType = MarketDirection.NONE;
        public Tick lastPeak;
        public Tick lastTriggerSquareOffTick;
        public MarketDirection lastOrderSquareOffTriggerDirection = MarketDirection.NONE;
        public StockOrder lastOrder;
        public int DayTotalTickConsideredCounts = 0;
        public DateTime TimeOfFirstTickOfDay;
        public DateTime TimeOfLastTickOfDay;

        // Charting
        DateTime DayToday = new DateTime(2011, 5, 9);
        public int DayChartTickCounter = 0;

        // Stats
        public class DayNett
        {
            public DateTime Day;
            public double Nett;

            public DayNett(DateTime day, double nett)
            {
                Day = day;
                Nett = nett;
            }
        }
        List<DayNett> dayNett = new List<DayNett>(1000);
        List<DayNett> expectedDayNett = new List<DayNett>(1000);
        bool isEODWindupDone = false;

        EOPTradeStats EopTradeStats = new EOPTradeStats();

        Tick lastTick;

        // Discontinuous Price tick stats
        int discontinuousMaxTickNum = 0;
        int discontinuousMinTickNum = 0;
        //double[] discontinuityThresholds = { 5, 3, 2, 1.5, 1.4, 1.4, 1.3, 1.3, 1.2, 1.2, 1.2 };

        public void AddTick(DerivativeQuoteInformation di, bool bMarketTick, bool? marketClosing)
        {
            if (marketClosing == null)
            {
                marketClosing = MarketUtils.IsTimeAfter315(di.UpdateTime);
            }

            // Write to tickfile
            if (!mIsReplayMode && bMarketTick && mPercChangeThresholdForMarketDirection == 0.1)
            {
                using (FileStream fs = new FileStream(mTickFileName, FileMode.Append, FileAccess.Write))
                {
                    using (StreamWriter sw = new StreamWriter(fs))
                    {
                        string str = di.UpdateTime.ToString("yyyyMMdd:HH:mm:ss") + ";" + di.LastTradedPriceDouble + ";" + di.BidPriceDouble +
                            ";" + di.OfferPriceDouble + ";" + di.BidQuantityInt + ";" + di.OfferQuantityInt + ";" + di.VolumeTradedInt;
                        sw.WriteLine(str);
                    }
                }
            }

            // di is the new tick here
            Tick tick = new Tick()
            {
                Di = di,
                MarketDirection = MarketDirection.NONE,
                PercChangeFromLastTrendingTick = 0,
                Order = null,
                TickNumber = mTotalTickCount
            };

            DerivativeQuoteInformation currentDi = tick.Di;

             // Stats
            double tradeValue = GetTradeValueForAnalysis(di);
                
            mTimeOfLastTick = tick.Di.OriginalTime;

            // First tick of this algo, initialize daytoday and other variables
            if (mTotalTickCount == 1)
            {
                mTimeOfFirstTick = tick.Di.OriginalTime;
                DayToday = currentDi.OriginalTime;
                
                isEODWindupDone = false;
                EodTradeStats = new EODTradeStats();

                FileTracing.TraceVerbose("First tick of day. No analysis");

                if (MinTick == null || MaxTick == null)
                {
                    MinTick = MaxTick = tick;
                }
                TimeOfFirstTickOfDay = currentDi.OriginalTime;
            }
            mAverageTradePrice += tradeValue;

            FileTracing.TraceVerbose("New Tick. LTP = " + tradeValue + ". OFFER=" + di.OfferPriceDouble + ". BID=" + di.BidPriceDouble);


            bool isNextDay = (currentDi.UpdateTime.Date != DayToday.Date);// && currentDi.UpdateTime.Month != DayToday.Month);

            bool doesNeedEODWindup = mIsMarketClosingSquareOff && (marketClosing.Value || isNextDay) && !isEODWindupDone;

            // if marketclosing then squareoff positions and dont take any new positions
            if (doesNeedEODWindup)
            {
                int numOpenPositions = TotalBuyTrades - TotalSellTrades;

                if (numOpenPositions != 0)
                {
                    // EOD Squareoff

                    bool isBuy = numOpenPositions < 0 ? true : false;

                    tick.MarketDirection = isBuy ? MarketDirection.UP : MarketDirection.DOWN;

                    Analyze(tick, marketClosing.Value);
                }

                if (EodTradeStats == null)
                    EodTradeStats = new EODTradeStats();

                EodTradeStats.number_of_ticks = DayTotalTickConsideredCounts;

                EopTradeStats.number_of_ticks += EodTradeStats.number_of_ticks;

                //if(isNextDay)
                //{
                //    TimeOfLastTickOfDay = lastTick.Di.OriginalTime;
                //}
                //else
                //{
                //    TimeOfLastTickOfDay = currentDi.OriginalTime;
                //}
                //EopTradeStats

                EodTradeStats.inmarket_time_in_minutes = (int)(TimeOfLastTickOfDay - TimeOfFirstTickOfDay).TotalMinutes;

                EopTradeStats.inmarket_time_in_minutes += EodTradeStats.inmarket_time_in_minutes;

                EopTradeStats.num_days++;

                EodTradeStats.status_update_time = DateTime.Now;
                EodTradeStats.trade_date = DayToday.Date;
                EodTradeStats.algo_id = 10;
                EodTradeStats.contract_name = mStockCode;

                EodTradeStats.average_profit_pertrade = EodTradeStats.num_profit_trades == 0 ? 0 : EodTradeStats.average_profit_pertrade / EodTradeStats.num_profit_trades;
                EodTradeStats.average_loss_pertrade = EodTradeStats.num_loss_trades == 0 ? 0 : EodTradeStats.average_loss_pertrade / EodTradeStats.num_loss_trades;

                // Because money invested is only 25% (margin) for derivatives, thus multiplication factor of 4
                EodTradeStats.roi_percentage = (EodTradeStats.expected_profit / lastTick.Di.LastTradedPriceDouble) * 100 * 4;
                EodTradeStats.market_direction_percentage = mPercChangeThresholdForMarketDirection;

                // Insert EodSummary into DB
                EodTradeStats.Persist();

                // Reset the variables for next day
                lastOrderSquareOffTriggerDirection = MarketDirection.NONE;
                DayTotalTickConsideredCounts = 0;
                MinTick = MaxTick = lastPeak = lastTriggerSquareOffTick = null;

                lastPeakType = MarketDirection.NONE;
                lastOrderSquareOffTriggerDirection = MarketDirection.NONE;
                //lastOrder = null;

                // Stats
                dayNett.Add(new DayNett(DayToday, TotalProfit));
                expectedDayNett.Add(new DayNett(DayToday, ExpectedNettProfit));

                isEODWindupDone = true;
            }

            // Will never hit in EOD data, because mIsMarketClosingSquareOff is false and hence isEODWindupDone is false always
            if (isNextDay && isEODWindupDone)
            {
                // Start of next day
                isEODWindupDone = false;
                DayToday = currentDi.OriginalTime;

                EodTradeStats = new EODTradeStats();

                FileTracing.TraceVerbose("First tick of day. No analysis");

                if (MinTick == null || MaxTick == null)
                {
                    MinTick = MaxTick = tick;
                }
                TimeOfFirstTickOfDay = currentDi.OriginalTime;

                DayTotalTickConsideredCounts++;

                TimeOfLastTickOfDay = currentDi.OriginalTime;
            }

            // Charting tricks
            if (isNextDay)
            {
                lastTick = tick;
                DayChartTickCounter = 0;
            }
            else
            {
                DayChartTickCounter++;
            }

            currentDi.UpdateTime += new TimeSpan(0, DayChartTickCounter * 1, 0);

            // keep adding to global tick list
            //allTicks.Add(tick);
            

            // After market hours, same day
            //if (!(!isNextDay && !isEODWindupDone))
            //    return;

            // Analyzed ticks counting
            lastTick = tick;
            DayTotalTickConsideredCounts++;
            TimeOfLastTickOfDay = currentDi.OriginalTime;
            

            // Peak Detection and Order Placement

            DerivativeQuoteInformation maxDi = MaxTick.Di;
            DerivativeQuoteInformation minDi = MinTick.Di;
            double percChangeFromMax = 100 * ((GetTradeValueForAnalysis(di) - GetTradeValueForAnalysis(maxDi)) / GetTradeValueForAnalysis(maxDi));
            double percChangeFromMin = 100 * ((GetTradeValueForAnalysis(di) - GetTradeValueForAnalysis(minDi)) / GetTradeValueForAnalysis(minDi));

            if(percChangeFromMax >= 0)
            {
                // New Max
                MaxTick = tick;
                tick.MarketDirection = MarketDirection.UP;
            }
            else if(percChangeFromMin <= 0)
            {
                // New Min
                MinTick = tick;
                tick.MarketDirection = MarketDirection.DOWN;
            }


            bool isPeakDetected = false;
            bool isSquareOffPointDetected = false;
            bool isSquareOff = false;

            // detect peaks and square off trigger points
            if (lastPeakType == MarketDirection.DOWN)
            {
                if (mIsSquareOffTrigger && percChangeFromMax < 0 && Math.Abs(percChangeFromMax) >= mPercChangeThresholdForSquareOff)
                {
                    int numOpenPositions = Math.Abs(TotalBuyTrades - TotalSellTrades);

                    if (numOpenPositions != 0)
                    {
                        double orderPrice = tick.Di.BidPriceDouble;
                        double profitPoints = orderPrice - mOrders[0].OrderPrice;
                        double brokerage = (1.4 * mBrokerage * orderPrice / 100);

                        double nettProfit = profitPoints - brokerage;

                        double profitPerc = 100 * (nettProfit / orderPrice);

                        if (!mIsMinProfitMust || profitPerc > mMinimumProfitPercentage)
                        {
                            // SquareOff point reached
                            tick.MarketDirection = MarketDirection.DOWN;
                            tick.PercChangeFromLastTrendingTick = percChangeFromMax;

                            isSquareOffPointDetected = true;
                            lastTriggerSquareOffTick = MaxTick;
                            lastOrderSquareOffTriggerDirection = tick.MarketDirection;
                            Analyze(tick, marketClosing.Value);
                        }
                    }
                }

                double percPriceDiff = (tick.Di.BidPriceDouble - lastOrder.OrderPrice) / lastOrder.OrderPrice;
                bool canOrderThisAnalysis = (lastOrderSquareOffTriggerDirection != MarketDirection.DOWN) || (percPriceDiff > mBrokerage);

                if (percChangeFromMax < 0 && Math.Abs(percChangeFromMax) >= mPercChangeThresholdForMarketDirection)
                {
                    // Record tick abberations
                    if (Math.Abs(percChangeFromMax) - mPercChangeThresholdForMarketDirection > 0.4)
                    {
                        Logger.LogWarningMessage(string.Format("{3}: {0} : PriceTick non-continuous {1}: PercChangeFromMax = {2} times.",
                            tick.Di.OriginalTime.ToString(), discontinuousMaxTickNum++, percChangeFromMax / mPercChangeThresholdForMarketDirection, mStockCode));
                    }
                    // New top found
                    lastPeakType = MarketDirection.UP;
                    lastPeak = MaxTick;

                    tick.MarketDirection = MarketDirection.DOWN;
                    tick.PercChangeFromLastTrendingTick = percChangeFromMax;
                    MinTick = tick;

                    allPeaks.Add(mTotalTickCount, lastPeak);
                    isPeakDetected = true;

                    if (canOrderThisAnalysis)
                    {
                        isSquareOff = Analyze(tick, marketClosing.Value);
                        lastOrderSquareOffTriggerDirection = MarketDirection.NONE;
                    }
                }
            }
            else if (lastPeakType == MarketDirection.UP)
            {
                // Look for squareoff
                if (mIsSquareOffTrigger && percChangeFromMin > 0 && Math.Abs(percChangeFromMin) >= mPercChangeThresholdForSquareOff)
                {
                    int numOpenPositions = Math.Abs(TotalBuyTrades - TotalSellTrades);

                    if (numOpenPositions != 0)
                    {
                        double orderPrice = tick.Di.OfferPriceDouble;
                        double profitPoints = orderPrice - mOrders[0].OrderPrice;
                        double brokerage = (1.4 * mBrokerage * orderPrice / 100);

                        double nettProfit = profitPoints - brokerage;

                        double profitPerc = 100 * (nettProfit / orderPrice);

                        if (!mIsMinProfitMust || profitPerc > mMinimumProfitPercentage)
                        {
                            // SquareOff point reached
                            tick.MarketDirection = MarketDirection.UP;
                            tick.PercChangeFromLastTrendingTick = percChangeFromMin;

                            isSquareOffPointDetected = true;
                            lastTriggerSquareOffTick = MinTick;
                            lastOrderSquareOffTriggerDirection = tick.MarketDirection;

                            Analyze(tick, marketClosing.Value);
                        }
                    }
                }

                double percPriceDiff = (lastOrder.OrderPrice - tick.Di.OfferPriceDouble) / lastOrder.OrderPrice;
                bool canOrderThisAnalysis =  (lastOrderSquareOffTriggerDirection != MarketDirection.UP) || (percPriceDiff > mBrokerage);

                // Peak - New trades or pending squareoffs
                if (percChangeFromMin > 0 && Math.Abs(percChangeFromMin) >= mPercChangeThresholdForMarketDirection)
                {
                    // Record tick abberations
                    if (Math.Abs(percChangeFromMin) - mPercChangeThresholdForMarketDirection > 0.4)
                    {
                        Logger.LogWarningMessage(string.Format("{3}: {0} : PriceTick non-continuous {1}: PercChangeFromMin = {2} times.",
                            tick.Di.OriginalTime.ToString(), discontinuousMinTickNum++, percChangeFromMin / mPercChangeThresholdForMarketDirection, mStockCode));
                    }

                    // New bottom found
                    lastPeakType = MarketDirection.DOWN;
                    lastPeak = MinTick;

                    tick.MarketDirection = MarketDirection.UP;
                    tick.PercChangeFromLastTrendingTick = percChangeFromMin;
                    MaxTick = tick;


                    allPeaks.Add(mTotalTickCount, lastPeak);
                    isPeakDetected = true;

                    if (canOrderThisAnalysis)
                    {
                        isSquareOff = Analyze(tick, marketClosing.Value);

                        lastOrderSquareOffTriggerDirection = MarketDirection.NONE;
                    }
                }
            }
            else if (lastPeakType == MarketDirection.NONE)
            {
                // 1st peak
                double percDiffMinMax = 100 * ((GetTradeValueForAnalysis(MaxTick.Di) - GetTradeValueForAnalysis(MinTick.Di)) / GetTradeValueForAnalysis(MinTick.Di));
                
                if (Math.Abs(percDiffMinMax) >= mPercChangeThresholdForMarketDirection)
                {
                    // Set last peak as reverse of current tick direction
                    lastPeakType = tick.MarketDirection == MarketDirection.UP ? MarketDirection.DOWN : MarketDirection.UP;

                    if (lastPeakType == MarketDirection.DOWN)
                    {
                        tick.PercChangeFromLastTrendingTick = percDiffMinMax;
                        lastPeak = MinTick;
                    }
                    else
                    {
                        tick.PercChangeFromLastTrendingTick = -percDiffMinMax;
                        lastPeak = MaxTick;
                    }
                    allPeaks.Add(mTotalTickCount, lastPeak);
                    isPeakDetected = true;

                    isSquareOff = Analyze(tick, marketClosing.Value);
                }
            }

            // If last order was square offf in this tick, then take 1 more open position
            //if(mIsDoubleRide && isSquareOff)
            //    Analyze(tick, marketClosing.Value);

            //if (isSquareOffPointDetected)
            //{
            //    lastOrderSquareOffTriggerDirection = tick.MarketDirection;
            //}


            // do charting here
            if (isPeakDetected)
            {
                // chart out peak and order ticks
                // tick = order tick
                // allPeaks[allPeaks.Count-1] is peak tick

                DerivativeQuoteInformation peakDi = lastPeak.Di;
                bool isBottom = lastPeak.MarketDirection == MarketDirection.DOWN ? true: false;

                ChartTickPoint peakPoint = new ChartTickPoint(peakDi.UpdateTime, peakDi.LastTradedPriceDouble, isBottom, ChartTickType.Peak);
                _chartDelegate(peakPoint);

                bool isBuy = lastOrder.OrderPosition == Position.Buy ? true : false;

                ChartTickPoint orderPoint = new ChartTickPoint(tick.Di.UpdateTime, tick.Di.LastTradedPriceDouble, isBuy, ChartTickType.Order);
                _chartDelegate(orderPoint);
            }


            if (isSquareOffPointDetected)
            {
                DerivativeQuoteInformation peakDi = lastTriggerSquareOffTick.Di;
                bool isBottom = lastPeak.MarketDirection == MarketDirection.DOWN ? true : false;

                ChartTickPoint peakPoint = new ChartTickPoint(peakDi.UpdateTime, peakDi.LastTradedPriceDouble, isBottom, ChartTickType.SquareOffPoint);
                _chartDelegate(peakPoint);

                bool isBuy = lastOrder.OrderPosition == Position.Buy ? true : false;

                ChartTickPoint orderPoint = new ChartTickPoint(tick.Di.UpdateTime, tick.Di.LastTradedPriceDouble, isBuy, ChartTickType.Order);
                _chartDelegate(orderPoint);

            }

            {
                // chart out the tick
                // tick = normal tick
                //ChartTickPoint pricePoint = new ChartTickPoint(tick.Di.UpdateTime, tick.Di.LastTradedPriceDouble, false, ChartTickType.Price);
                //addDataDel(point);
                //dt = dt.AddMinutes(x);
                //_chartDelegate(pricePoint);
               //mChart.AddTick(tick.Di.UpdateTime, tick.Di.LastTradedPriceDouble);
                //AddTickToChart(tick.Di.UpdateTime, tick.Di.LastTradedPriceDouble);
            }
            //mChart.Visible = true;
        }

        bool Analyze(Tick tick, bool marketClosing)
        {
            //if (ticks.Count < 2)
            //{
            //    FileTracing.TraceVerbose("Cannot analyze, since less than 2 ticks");
            //    return;
            //}
            DerivativeQuoteInformation di = tick.Di;

            bool isBuy = tick.MarketDirection == MarketDirection.UP;

            // Just Reverse the direction
            //isBuy = !isBuy;

            //double orderPrice = isBuy ? di.OfferPriceDouble: di.BidPriceDouble;
            //double percPriceDiff = (orderPrice - lastOrder.OrderPrice) / lastOrder.OrderPrice;

            //bool canOrderThisAnalysis = !wasLastOrderSquareOffTriggered || (percPriceDiff > mBrokerage);
           
            int numOpenPositions = Math.Abs(TotalBuyTrades - TotalSellTrades);

            bool isMoreBuyOpenPositionsThanSell = TotalBuyTrades > TotalSellTrades;

            bool isSquareOff = !((isMoreBuyOpenPositionsThanSell && isBuy) || (!isMoreBuyOpenPositionsThanSell && !isBuy));

            if (numOpenPositions == 0)
                isSquareOff = false;

            bool isLimitAvailable = (numOpenPositions < mMaxTotalPositions);


            //if (!canOrderThisAnalysis)
            //    return isSquareOff;

            //bool isExtraTrade = isBuy && isMoreBuyOpenPositions;


            //if (isBuy && isMoreBuyOpenPositions)
            //{
            //    // Apply LongCeiling price filter
            //    if (mLongCeilingPrice > 0 && orderPrice > mLongCeilingPrice)
            //    {
            //        FileTracing.TraceVerbose("FreshLong: Discarding : LongCeilingPrice hit");
            //        orderValid = false;
            //    }
            //}
            //else if (!isBuy && !isMoreBuyOpenPositions)
            //{


            //}

            

            if (isSquareOff || isLimitAvailable)
            {
                Position orderDirection = isBuy ? Position.Buy : Position.Sell;
                StockOrder newOrder = new StockOrder(orderDirection, 0);
                if (PlaceOrder(newOrder, di))
                {
                    if (marketClosing)
                    {
                        newOrder.ExpectedOrderPrice = newOrder.OrderPrice;
                    }
                    else
                    {
                        // Ideal conditions Order price setting
                        if (newOrder.OrderPosition == Position.Buy)
                        {
                            newOrder.ExpectedOrderPrice = lastPeak.Di.LastTradedPriceDouble * (1 + mPercChangeThresholdForMarketDirection / 100);
                        }
                        else
                        {
                            newOrder.ExpectedOrderPrice = lastPeak.Di.LastTradedPriceDouble * (1 - mPercChangeThresholdForMarketDirection / 100);
                        }
                    }

                    FileTracing.TraceVerbose("Analyze: PlaceOrder Successful");

                    StockOrder squaredOffOrder = null;

                    // Get paired order
                    for (int i = mOrders.Count - 1; i >= 0; i--)
                    {
                        if (mOrders[i].OrderPosition != newOrder.OrderPosition)
                        {
                            squaredOffOrder = mOrders[i];
                            mOrders.RemoveAt(i);
                            break;
                        }
                    }

                    if (squaredOffOrder != null)
                    {
                        double profitPoints = squaredOffOrder.OrderPrice - newOrder.OrderPrice;
                        if (!isBuy)
                        {
                            profitPoints = -profitPoints;
                        }

                        if (profitPoints >= 0)
                        {
                            profitableOrders.Add(profitPoints);
                        }
                        

                        double brokerage = (1.4 * mBrokerage * newOrder.OrderPrice / 100);

                        double nettProfit = profitPoints - brokerage;

                        if (nettProfit >= 0)
                        {
                            profitableAfterBrokerageOrders.Add(nettProfit);
                        }
                        else
                        {
                            lossOrders.Add(nettProfit);
                        }

                        TotalProfit += nettProfit;
                        TotalBrokerage += brokerage;

                        FileTracing.TraceTradeInformation("SquareOff: Profit Amount (after brokerage) = " + profitPoints);

                        // Ideal conditions expected nettProfit
                        // Expected loss should be threshold percent at max in ideal conditions.
                        //double expectedProfit = -profitPoints - brokerage - (newOrder.OrderPrice * mPercChangeThresholdForMarketDirection);
                        double expectedProfitPoints = squaredOffOrder.ExpectedOrderPrice - newOrder.ExpectedOrderPrice;
                        if (!isBuy)
                        {
                            expectedProfitPoints = -expectedProfitPoints;
                        }

                        double expectedNettProfit = expectedProfitPoints - brokerage;

                        ExpectedNettProfit += expectedNettProfit;

                        // Expected profit should always be greater. If not, then either there is a bug in code or tick data is incorrect.
                        Debug.Assert(nettProfit-expectedNettProfit <= 0);

                        if (expectedNettProfit < 0)
                        {
                            //if (EodTradeStats.num_loss_trades == 0)
                            EodTradeStats.average_loss_pertrade += expectedNettProfit;
                            EodTradeStats.num_loss_trades++;

                            EopTradeStats.average_loss_pertrade += expectedNettProfit;
                            EopTradeStats.num_loss_trades++;
                            //EodTradeStats.average_loss_pertrade = (EodTradeStats.average_loss_pertrade + expectedNettProfit) / 2;
                        }
                        else if (expectedNettProfit > 0)
                        {
                            //if (EodTradeStats.num_profit_trades == 0)
                            EodTradeStats.average_profit_pertrade += expectedNettProfit;
                            EodTradeStats.num_profit_trades++;

                            EopTradeStats.average_profit_pertrade += expectedNettProfit;
                            EopTradeStats.num_profit_trades++;

                            //EodTradeStats.average_profit_pertrade = (EodTradeStats.average_profit_pertrade + expectedNettProfit) / 2;
                        }

                        EodTradeStats.expected_profit += expectedNettProfit;
                        
                    }
                    else
                    {
                        mOrders.Add(newOrder);
                    }

                    // Add to Global orders list
                    allOrders.Add(newOrder);

                    //allPeaks[mTotalTickCount].Order = newOrder;

                    lastOrder = newOrder;
                }
                else
                {
                    FileTracing.TraceVerbose("Analyze: PlaceOrder failed due to IOC");
                }
            }
            else
            {
                TotalTimesLimitShortage++;
                FileTracing.TraceInformation("Not sufficient limit to place order: Number " + TotalTimesLimitShortage);
            }

            return isSquareOff;
        }

        bool PlaceOrder(StockOrder orderToPlace, DerivativeQuoteInformation di)
        {
            Position position = orderToPlace.OrderPosition;
            BrokerErrorCode errorCode = BrokerErrorCode.Success;
            double executionPrice = -1;

            string orderReference = null;
            OrderDirection orderDirection;
            double orderPrice;
            bool hasOrderExecuted;
            int tradeQuantity = mQuantity;

            if (position == Position.Buy)
            {
                orderPrice = di.OfferPriceDouble;
                orderDirection = OrderDirection.BUY;
            }
            else if (position == Position.Sell)
            {
                orderPrice = di.BidPriceDouble;
                orderDirection = OrderDirection.SELL;
            }
            else
            {
                throw new NotSupportedException("Position=" + position);
            }

            //string str = DateTime.Now.ToLongDateString() + "T " + DateTime.Now.ToLongTimeString() + ". Placing order. Position=" + position.ToString() + ". BidPrice=" + orderPrice + ". Quantity=" + tradeQuantity;
            string str;
            //str = DateTime.Now.ToLongDateString() + " T " + di.UpdateTime.ToLongTimeString() + ". Placing order. Position=" + position.ToString() + ". BidPrice=" + orderPrice + ". Quantity=" + tradeQuantity;
            str = di.UpdateTime.ToLongDateString() + " " + di.UpdateTime.ToLongTimeString() + ". Placing order. Position=" + position.ToString() + ". OrderPrice=" + orderPrice + ". Quantity=" + tradeQuantity;
            FileTracing.TraceTradeInformation(str);
            //Beep(100, 50);

            // ---- Place order
            while (true)
            {
                if (mMock || mIsReplayMode)
                {
                    errorCode = BrokerErrorCode.Success;
                    orderReference = "Mock Order";
                }
                else
                {
                    errorCode = mBroker.PlaceDerivativeOrder(mStockCode,
                       tradeQuantity,
                       orderPrice,
                       OrderPriceType.LIMIT,
                       orderDirection,
                       mInstrumentType,
                       mExpiryDate,
                       OrderGoodNessType.IOC,
                       out orderReference);
                }

                if (errorCode == BrokerErrorCode.NotLoggedIn)
                {
                    mBroker.LogOut();
                    errorCode = mBroker.CheckAndLogInIfNeeded(true);
                    continue;
                }
                if (errorCode == BrokerErrorCode.Success)
                {
                    break;
                }
                // BUGBUGC: handle the exception, Insufficient funds etc. all
                throw new Exception("BSA:PlaceOrder:ErrorCode=" + errorCode);
            }

            // --- Check order status and execution price

            if (!(mMock || mIsReplayMode))
            {
                hasOrderExecuted = LoopUntilFutureOrderIsExecutedOrCancelled(orderReference, out executionPrice);
            }
            else
            {
                hasOrderExecuted = true;
            }

            // Get Actual execution price
            if (hasOrderExecuted)
            {
                orderToPlace.OrderPrice = executionPrice != -1 ? executionPrice : orderPrice;
                TotalTurnover += orderToPlace.OrderPrice * mQuantity;
                if (position == Position.Buy)
                {
                    TotalBuyTrades++;
                    EodTradeStats.num_trades++;
                    FileTracing.TraceTradeInformation("Trade Confirmed at " + orderToPlace.OrderPrice + ": Order Reference Number = " + orderReference);
                }
                else if (position == Position.Sell)
                {
                    TotalSellTrades++;
                    FileTracing.TraceTradeInformation("Trade Confirmed at " + orderToPlace.OrderPrice + ": Order Reference Number = " + orderReference);
                }

                return true;
            }
            else
            {
                TotalFailedOrders++;
                FileTracing.TraceTradeInformation("No Trade");
                return false;
            }
        }

        private bool LoopUntilFutureOrderIsExecutedOrCancelled(string orderReference, out double executionPrice)
        {
            executionPrice = -1;
        
            while (true)
            {
                DateTime fromDate = DateTime.Now;
                DateTime toDate = DateTime.Now;
                OrderStatus orderStatus;
                BrokerErrorCode errorCode = BrokerErrorCode.Success;

                // Incase of mock or testing replay
                if (mMock || mIsReplayMode)
                {
                    orderStatus = OrderStatus.EXECUTED;
                }
                else
                {
                    errorCode = mBroker.GetOrderStatus(orderReference,
                                            mInstrumentType, fromDate, toDate, out orderStatus);
                }

                // If fatal error then come out
                if (LoginUtils.IsErrorFatal(errorCode))
                {
                    FileTracing.TraceOut("PlaceOrder: Fatal Error " + errorCode.ToString());
                    return false;
                }

                // EXECUTED
                if (orderStatus.Equals(OrderStatus.EXECUTED))
                {
                    int retryCount = 0;

                    if (!(mMock || mIsReplayMode))
                    {
                        // Get actual execution price
                        do
                        {
                            executionPrice = mBroker.GetTradeExecutionPrice(fromDate, toDate, mInstrumentType, orderReference);
                        } while (executionPrice == -1 && retryCount++ <= 2);
                    }
                    return true;
                }


                if (orderStatus == OrderStatus.REJECTED ||
                    orderStatus == OrderStatus.CANCELLED ||
                    orderStatus == OrderStatus.EXPIRED)
                {
                    return false;
                }
            }
        }


        // ------- ITradingAlgo Interface methods -------- //

        public BrokerErrorCode Prolog()
        {
            BrokerErrorCode errorCode = BrokerErrorCode.Success;
            string traceString = "\n\n----- Starting MKD_FutureTrader Thread ----- \n";

            traceString += "Algo10_BuySellAnalyzer.Prolog :: ENTER\n";
            //traceString += "MarketDirectionThreshold = " + mPercChangeThresholdForMarketDirection;

            FileTracing.TraceOut(traceString);

            if (!mIsReplayMode)
            {
                errorCode = mBroker.CheckAndLogInIfNeeded(false);
                if (errorCode != BrokerErrorCode.Success)
                {
                    FileTracing.TraceOut("ERROR: LogIn(). BrokerErrorCode=" + errorCode.ToString());
                }
            }
            try
            {
                if (!File.Exists(mPositionsFile))
                {
                    FileStream fs = File.Create(mPositionsFile);
                    fs.Close();
                }
                using (FileStream fs = new FileStream(mPositionsFile, FileMode.Open, FileAccess.Read))
                {
                    using (StreamReader sr = new StreamReader(fs))
                    {
                        string s = null;
                        s = sr.ReadLine();
                        string[] parts;

                        if (s != null)
                        {
                            parts = s.Split(';');

                            double.TryParse(parts[0], out mCummulativeProfitBooked);

                            do
                            {
                                s = sr.ReadLine();
                                if (s != null)
                                {
                                    parts = s.Split(';');
                                    Position position = (Position)Enum.Parse(typeof(Position), parts[0]);
                                    double price = 0;
                                    if (double.TryParse(parts[1], out price))
                                    {
                                        StockOrder order = new StockOrder(position, price);
                                        // MGOYAL: BUGBUG
                                        // This is a hack to get AvgTradePrice to be non-zero in case of off-market run
                                        // and some initial orders being present
                                        mAverageTradePrice = price;
                                        mOrders.Add(order);
                                        Console.WriteLine("InitialOrder = " + order);
                                    }
                                }
                            } while (s != null);
                        }
                    }
                }
            }
            catch(IOException e)
            {
                Logger.LogException(e);
            }

            traceString = "Algo10_BuySellAnalyzer.Prolog :: EXIT";
            FileTracing.TraceOut(traceString);

            return errorCode;
        }

        public BrokerErrorCode RunCoreAlgo()
        {
            BrokerErrorCode errorCode = BrokerErrorCode.Success;
            // replay bsa text file
            if (mIsReplayMode && !bReplayDone)
            {
                #region ReplayBSATicksFile
                bReplayDone = true;

                List<DerivativeQuoteInformation> dqis = new List<DerivativeQuoteInformation>(10000);

                using (FileStream fs = new FileStream(mReplayTickFile, FileMode.Open, FileAccess.Read))
                {
                    using (StreamReader sr = new StreamReader(fs))
                    {
                        string s = null;
                        do
                        {
                            s = sr.ReadLine();
                            if (s != null)
                            {
                                string[] parts = s.Split(';');

                                IFormatProvider culture = new CultureInfo("en-US", true);
                                DateTime dt = DateTime.ParseExact(parts[0], "yyyyMMdd:HH:mm:ss", culture);
                                int volumeTraded = 0;
                                if (parts.Length == 7)
                                {
                                    volumeTraded = int.Parse(parts[6]);
                                }

                                DerivativeQuoteInformation dqi = new DerivativeQuoteInformation(
                                    dt,
                                    double.Parse(parts[1]),
                                    double.Parse(parts[2]),
                                    double.Parse(parts[3]),
                                    int.Parse(parts[4]),
                                    int.Parse(parts[5]),
                                    volumeTraded
                                    );

                                // Add to list of DQI 
                                dqis.Add(dqi);

                                if (dqis.Count > 100000)
                                    break;
                            }

                        } while (s != null);
                    }
                }

                // Run for each quote tick now out of all collected from the replay file
                foreach (DerivativeQuoteInformation dqi in dqis)
                {
                    mTotalTickCount++;

                    AddTick(dqi, true, null);

                    ltp = dqi.LastTradedPriceDouble;
                }


                #endregion
            }
            else if(!mIsReplayMode)
            {
                #region ActualAlgoLoop
                // run bsa analyzer in loop
                DerivativeQuoteInformation di;
                errorCode = mBroker.GetDerivativeQuote(mStockCode,
                     mInstrumentType,
                     mExpiryDate,
                     0,
                     out di);

                if (errorCode.Equals(BrokerErrorCode.Success))
                {
                    mTotalTickCount++;
                    AddTick(di, true, null);
                    ltp = di.LastTradedPriceDouble;
                }
                else
                {
                    FileTracing.TraceOut("MKD_FutureTrader: " + errorCode.ToString());
                }
                #endregion
            }
            return errorCode;
        }

        public BrokerErrorCode Epilog()
        {
            BrokerErrorCode errorCode = BrokerErrorCode.Success;
            string traceString;
            //traceString = "Total Trades : \n" + "Buy: " + TotalBuyTrades + ", Sell: " + TotalSellTrades + "\n";
            //double tradeBrokerage = 0.7 * mBrokerage / 100 * TotalTurnover;
            //traceString += "Total brokerage : " + tradeBrokerage + "\n";
            //traceString += "Total Profit (after brokerage) : " + (TotalProfit - tradeBrokerage).ToString() + "\n";
            //traceString += "Current open positions:\n";
            //foreach (Order o in GetCurrentOpenPositions())
            //{
            //    traceString += "OrderDetails: " + o.ToString() + "\n";
            //}

            //FileTracing.TraceOut(traceString);

            // Write the open positions back into file for the next session
            List<StockOrder> openPositions = GetCurrentOpenPositions();
            AlgoRunStats stats = GetEODStats(ltp);
            ProfitLossStats plStats = stats.plStats;
            ProgramStats progStats = stats.progStats;

            mCummulativeProfitBooked += plStats.profit;
            mNettFruitIfSquaredOffAtLTP = mCummulativeProfitBooked + plStats.outstanding;

            // Write back open positions file
            //using (FileStream fs = new FileStream(mPositionsFile, FileMode.Create, FileAccess.Write))
            //{
            //    using (StreamWriter sw = new StreamWriter(fs))
            //    {
            //        string str = mCummulativeProfitBooked + ";" + mNettFruitIfSquaredOffAtLTP;
            //        sw.WriteLine(str);
            //        foreach (StockOrder order in openPositions)
            //        {
            //            str = order.OrderPosition + ";" + order.OrderPrice;
            //            sw.WriteLine(str);
            //        }
            //    }
            //}

            TraceSummary(ltp);
            //FileTracing.FlushTraces();
            
            // Save a backup of the positions, trace and ticks file
            if (!mIsReplayMode)
            {
                string backupPath = SystemUtils.GetStockFilesBackupLocation();

                string dateWiseFolder = DateTime.Today.ToString("yyyyMMdd");

                string fullBackupPath = backupPath + "\\" + dateWiseFolder + "\\" + Guid.NewGuid().ToString() + "\\";


                if(!Directory.Exists(fullBackupPath))
                {
                    Directory.CreateDirectory(fullBackupPath);
                }

                

                if(File.Exists(mTickFileName)) File.Copy(mTickFileName, fullBackupPath + mStockCode + "-Ticks.txt", false);
                if (File.Exists(mTraceFileName)) File.Copy(mTraceFileName, fullBackupPath + mStockCode + "-Trace.txt", false);
                if (File.Exists(mPositionsFile)) File.Copy(mPositionsFile, fullBackupPath + mStockCode + "-Positions.txt", false);
            }


            // Program stats
            traceString = "Total failed orders : " + progStats.failedOrders + "\n";
            FileTracing.TraceOut(traceString);
            FileTracing.TraceOut("----- Exiting MKD_FutureTrader Thread ----- \n");
            
            FileTracing.TraceInformation(" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ");
            FileTracing.TraceOut("");
            FileTracing.TraceOut("");

            return errorCode;
        }

        public int GetSleepTimeInMilliSecs()
        {
            // 2 second sleep
            return 2000;
        }

        // ------- Tracing tradeSummary  methods -------- //

        public List<StockOrder> GetCurrentOpenPositions()
        {
            List<StockOrder> list = new List<StockOrder>();
            foreach (StockOrder o in mOrders)
            {
                list.Add(new StockOrder(o));
            }
            return list;
        }

        public AlgoRunStats GetEODStats(double ltp)
        {
            AlgoRunStats stats = new AlgoRunStats();
            ProfitLossStats plStats = stats.plStats;
            ProgramStats progStats = stats.progStats;

            plStats.buyTrades = TotalBuyTrades;
            plStats.sellTrades = TotalSellTrades;
            double brokerage = TotalBrokerage * mQuantity;
            plStats.brokerage = brokerage;
            double profitAmt = TotalProfit * mQuantity;
            plStats.profit = profitAmt;

            double buyAmt = 0, sellAmt = 0;
            int buyPos = 0, sellPos = 0;

            foreach (StockOrder order in GetCurrentOpenPositions())
            {
                if (order.OrderPosition == Position.Buy)
                {
                    buyAmt += order.OrderPrice;
                    buyPos++;
                }
                else if (order.OrderPosition == Position.Sell)
                {
                    sellAmt += order.OrderPrice;
                    sellPos++;
                }
            }

            buyAmt /= (buyPos > 0) ? buyPos : 1;
            sellAmt /= (sellPos > 0) ? sellPos : 1;

            int loop = Math.Min(buyPos, sellPos);

            buyPos -= loop;
            sellPos -= loop;

            double nettOpenPosition = (sellAmt - buyAmt) * loop;
            if (buyPos > sellPos)
            {
                nettOpenPosition += (ltp - buyAmt) * buyPos;
            }
            else
            {
                nettOpenPosition += (sellAmt - ltp) * sellPos;
            }
            // 1.4 is for same day squareopff trades
            brokerage = (mBrokerage / 100 * ltp) * (1.4 * loop + Math.Abs(buyPos - sellPos));
            nettOpenPosition -= brokerage;
            nettOpenPosition *= mQuantity;
            plStats.outstanding = nettOpenPosition;
            double nettDay = profitAmt + nettOpenPosition;
            plStats.nettAfterSquareOff = nettDay;

            progStats.failedOrders = TotalFailedOrders;
            return stats;
        }

        public void TraceSummary(double ltp)
        {
            mAverageTradePrice = mTotalTickCount == 0 ? 0 : mAverageTradePrice / mTotalTickCount;

            ltp = ltp != 0 ? ltp : mAverageTradePrice != 0 ? mAverageTradePrice : 1;
            FileTracing.TraceInformation("");
            FileTracing.TraceInformation(" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ");
            //FileTracing.TraceInformation(string.Format("IsDoubleRide = {0}", mIsDoubleRide));
            FileTracing.TraceInformation(string.Format("Algo = {0}", 10));
            FileTracing.TraceInformation(string.Format("IsMarketClosingSquareOff = {0}", mIsMarketClosingSquareOff));
            FileTracing.TraceInformation(string.Format("MinProfitPercentageIfSquareOffEnabled(Enabled={0}) = {1}", mIsMinProfitMust, mMinimumProfitPercentage));
            FileTracing.TraceInformation(string.Format("SquareOffThreshold(Enabled={0}) = {1}", mIsSquareOffTrigger, mPercChangeThresholdForSquareOff));
            FileTracing.TraceInformation(string.Format("MarketDirectionThreshold = {0}", mPercChangeThresholdForMarketDirection));
            FileTracing.TraceInformation(string.Format("BrokerageRate = {0}", mBrokerage));
            FileTracing.TraceInformation(" \n\n-----------------------------------------------\n\n "); 
            FileTracing.TraceInformation(string.Format("Trades: Buy = {0} Sell = {1}", TotalBuyTrades, TotalSellTrades));
            FileTracing.TraceInformation(string.Format("Trades: Profitable = {0} Profitable(AfterBrokerage) = {1} Loss = {2}", 
                profitableOrders.Count, profitableAfterBrokerageOrders.Count, lossOrders.Count));
            FileTracing.TraceInformation("Total Trade Diff. amount (after brokerage) : " + TotalProfit);
            FileTracing.TraceInformation("Total Trade Diff. amount (after brokerage) %: " + (TotalProfit / ltp) * 100);
            double brokerage = TotalBrokerage * mQuantity;
            double profitAmt = TotalProfit * mQuantity;
            
            double TotalProfitWithoutBrokerage = profitAmt + brokerage;
            
            FileTracing.TraceInformation("Open Positions");
            double buyAmt = 0, sellAmt = 0;
            int buyPos = 0, sellPos = 0;
            // double sellAmt = 0;
            foreach (StockOrder order in GetCurrentOpenPositions())
            {
                FileTracing.TraceInformation("OrderDetails: " + order.ToString());
                if (order.OrderPosition == Position.Buy)
                {
                    buyAmt += order.OrderPrice;
                    buyPos++;
                }
                else if (order.OrderPosition == Position.Sell)
                {
                    sellAmt += order.OrderPrice;
                    sellPos++;
                }
            }

            buyAmt /= (buyPos > 0) ? buyPos : 1;
            sellAmt /= (sellPos > 0) ? sellPos : 1;

            int loop = Math.Min(buyPos, sellPos);

            buyPos -= loop;
            sellPos -= loop;

            double nettOpenPosition = (sellAmt - buyAmt) * loop;
            if (buyPos > sellPos)
            {
                nettOpenPosition += (ltp - buyAmt) * buyPos;
            }
            else
            {
                nettOpenPosition += (sellAmt - ltp) * sellPos;
            }
            double openPosBrokerage = (mBrokerage / 100 * ltp) * (1.4 * loop + Math.Abs(buyPos - sellPos));
            nettOpenPosition -= openPosBrokerage;
            FileTracing.TraceInformation("Outstanding Trade Diff. points (after brokerage) : " + nettOpenPosition);
            FileTracing.TraceInformation("LTP : " + ltp);
            nettOpenPosition *= mQuantity;
            FileTracing.TraceInformation("Nett outstanding Amount (after brokerage) : " + nettOpenPosition);
            double nettDay = profitAmt + nettOpenPosition;
            FileTracing.TraceInformation("Nett fruit points (if squared off at LTP's Trade Diff.) : " + nettDay / mQuantity);
            int marginPositions = Math.Max(mMaxLongPositions, mMaxShortPositions);
            double moneyInvested = mQuantity * marginPositions * ltp * 0.25;

            FileTracing.TraceInformation(" \n\n-----------------------------------------------\n\n ");
            FileTracing.TraceInformation("Money Invested : " + moneyInvested);
            TimeSpan tradeTimeWindow = mTimeOfLastTick - mTimeOfFirstTick;
            double numMonths = tradeTimeWindow.TotalHours / (24 * 30);
            numMonths = numMonths == 0 ? 1 : numMonths;
            FileTracing.TraceInformation(string.Format("StartTime: {0} EndTime:{1} NumDays:{2}", mTimeOfFirstTick.ToShortDateString(),
                mTimeOfLastTick.ToShortDateString(), numMonths * 30));

            string eachDayNett = "";
            foreach (DayNett nett in dayNett)
            {
                eachDayNett += nett.Nett + ",";
            }
            FileTracing.TraceInformation("Days Earnings: " + eachDayNett);

            string expectedEachDayNett = "";
            foreach (DayNett nett in expectedDayNett)
            {
                expectedEachDayNett += nett.Nett + ",";
            }
            FileTracing.TraceInformation("Days Expected Earnings: " + expectedEachDayNett);

            FileTracing.TraceInformation("");
            double NettFruitWithoutBrokerage = TotalProfitWithoutBrokerage + nettOpenPosition + openPosBrokerage;
            FileTracing.TraceInformation(" ******************************************************************* ");
            FileTracing.TraceInformation("");
            //FileTracing.TraceInformation("Total Profit (without open pos squareoff after brokerage) : " + profitAmt);
            FileTracing.TraceInformation("Nett fruit amount (without brokerage) : " + NettFruitWithoutBrokerage);
            FileTracing.TraceInformation("Nett fruit amount (without brokerage) %: " + (NettFruitWithoutBrokerage / moneyInvested) * 100);
            FileTracing.TraceInformation("");
            FileTracing.TraceInformation("Nett Brokerage : " + brokerage + openPosBrokerage);
            double expectedNettDay = ExpectedNettProfit * mQuantity + nettOpenPosition;
            FileTracing.TraceInformation("Nett expected fruit amount : " + expectedNettDay);
            FileTracing.TraceInformation("Nett fruit amount : " + nettDay);
            FileTracing.TraceInformation("");
            FileTracing.TraceInformation("");

            FileTracing.TraceInformation("Nett expected ROI percentage made : " + 100 * (expectedNettDay / moneyInvested));
            
            double nettPerc = 100 * nettDay / moneyInvested;
            FileTracing.TraceInformation("Nett ROI percentage made : " + nettPerc);
            FileTracing.TraceInformation("Nett ROI percentage made (per month) : " + nettPerc / numMonths);

            FileTracing.TraceInformation("");

            FileTracing.TraceInformation(" ******************************************************************* ");
            //FileTracing.TraceInformation(" \n\n-----------------------------------------------\n\n ");
            FileTracing.TraceInformation("");
            //FileTracing.TraceInformation(" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ");

            EopTradeStats.number_of_ticks = mTotalTickCount;
            EopTradeStats.num_days = mTotalTickCount; // EOD data
            EopTradeStats.inmarket_time_in_minutes = 0;

            // Persist end of period trade stats into DB
            EopTradeStats.start_date = mTimeOfFirstTick.Date;
            EopTradeStats.end_date = mTimeOfLastTick.Date;

            // Using Min because we may not be able to squareoff last trade. If last day's MarketClosing ticks not present in data
            EopTradeStats.num_trades = Math.Min(TotalBuyTrades, TotalSellTrades);
            EopTradeStats.roi_percentage = (ExpectedNettProfit / (mAverageTradePrice == 0 ? 1 : mAverageTradePrice)) * 100 * 4;
            EopTradeStats.status_update_time = DateTime.Now;
            EopTradeStats.market_direction_percentage = mPercChangeThresholdForMarketDirection;
            EopTradeStats.average_trade_price = mAverageTradePrice;
            EopTradeStats.expected_profit = ExpectedNettProfit;
            EopTradeStats.actual_profit = TotalProfit;
            EopTradeStats.contract_name = mStockCode;
            EopTradeStats.algo_id = 10;
            EopTradeStats.average_profit_pertrade = EopTradeStats.num_profit_trades == 0 ? 0 : EopTradeStats.average_profit_pertrade / EopTradeStats.num_profit_trades;
            EopTradeStats.average_loss_pertrade = EopTradeStats.num_loss_trades == 0 ? 0 : EopTradeStats.average_loss_pertrade / EopTradeStats.num_loss_trades;

            //EopTradeStats.num_days = (int)(mTimeOfLastTick - mTimeOfFirstTick).TotalDays;
            //EopTradeStats.inmarket_time_in_minutes = 0;
            //EopTradeStats.number_of_ticks = mTotalTickCount;
            if(mTotalTickCount > 0 )
            EopTradeStats.Persist();
        }

        #region Windows APIs

        [DllImport("Kernel32.dll", SetLastError = true)]
        public static extern Boolean Beep(UInt32 frequency, UInt32 duration);

        #endregion
    }
}
